Skip to content

fix(verifiers): reject non-finite judge scores as invalid output - #495

Open
andrewpark-scaleai wants to merge 2 commits into
mainfrom
andrew-park/judge-nonfinite-scores
Open

andrewpark-scaleai wants to merge 2 commits into
mainfrom
andrew-park/judge-nonfinite-scores

Conversation

@andrewpark-scaleai

@andrewpark-scaleai andrewpark-scaleai commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

What and why

A rubric judge's score that is not a finite number counted as a pass. _clamp01 is max(0.0, min(1.0, x)), and min(1.0, nan) returns 1.0, so a NaN criterion scored 1.0. Infinity, 1e999 and quoted strings such as "NaN" or "inf" did the same through float(). Under rubric_binary, aggregate_score turned them into a weighted score of 1.0. An integer too large for a float raised an uncaught OverflowError on the first attempt, with no retry. A judge that writes any of these inflates the eval score.

  • _parse_judge_results, which all four rubric formats use, now treats a score that is not a finite number as invalid judge output. The verifier then retries with a correction prompt and, after DEFAULT_MAX_RETRIES, raises as it does for other invalid output.
  • The correction prompt and JudgeResponseDiscrepancy name the problem directly ("Each score must be a finite number; criterion id(s) c2 were not"), so a response that was valid JSON is not told it was invalid JSON.
  • An integer past Python's digit limit is now an invalid-JSON retry instead of an uncaught ValueError.
  • hypothesis joins the dev extra for one property test over parse and aggregate.

Out of scope, and unchanged: a non-numeric score such as "abc" or null still reads as 0.0 in the partial and evidence formats and still crashes rubric_binary aggregation; the trajectory_mistakes parser still catches only JSONDecodeError.

How it was tested

  • tst/unit/task_step/verifiers/test_rubrics_verifier_nonfinite_scores.py covers bare tokens, quoted strings and overflowing integers across all four formats, plus the retry and give-up paths on the real RubricsVerifierTaskStep. The first commit adds it as a strict xfail: it xfails on the unfixed code. With the fix, all 54 tests pass, and against the unfixed source all 54 fail.
  • The Hypothesis property fails when the fix is reverted, with the first counterexample [(0.0, ('NaN', False))].
  • pytest tst/unit/task_step tst/unit/env/task_step: 2420 passed. make lint passes. check_plugin_api.py --base origin/main: no break to the plugin surface.

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge; no actionable issues were found.

Summary

The PR rejects non-finite judge scores before rubric scores are built.

  • Rubric verifiers reject scores that are not finite numbers.

No actionable issues were found. Tests were inspected but not run during this review.

andrewpark-scaleai explicitly identifies nonnumeric scores and the separate trajectory-mistakes parser as unchanged and out of scope.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Judge response] --> B[Parse JSON and check criterion ids]
  B --> C{Scores are finite?}
  C -->|Yes| D[Build rubric rows and aggregate]
  C -->|No| E{Attempts remain?}
  E -->|Yes| F[Ask judge to correct scores]
  F --> A
  E -->|No| G[Raise error without saving a score]
Loading

Reviews (1) · Last reviewed commit: "fix(verifiers): reject non-finite judge ..." · Reviewed by Greptile

andrewpark-scaleai and others added 2 commits November 4, 2026 13:03
The rubric parsers score whatever float() makes of a judge's score: a bare or
quoted NaN or Infinity can pass a row or push a weighted average to 1.0, and an
integer literal too large for a float crashes the step without a retry. Strict
xfail across every rubric format until the fix lands.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The shared rubric parse now returns a discrepancy naming each criterion whose
score is not a finite number (NaN, Infinity, overflowing literals, or strings
that parse to one), so the verifier asks the judge again instead of clamping
the score into a pass. The correction prompt says each score must be a finite
number, since the response was valid JSON. An integer past Python's digit
limit is now an invalid-JSON retry instead of an uncaught ValueError.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@andrewpark-scaleai
andrewpark-scaleai requested a review from a team as a code owner November 4, 2026 21:11
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedpypi/​hypothesis@​6.170.149810010010070

View full report

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant